package montecarlo.algorithms;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;


public class Multi extends AbstractClassMC {
	
	/**
	 * Multi-threading function. Calls the algorithm.
	 */
	@Override
	public double monteCarloStandardOption(final String callPutFlag, final double s,
			final double x, final double r, final double t, final double b, final double v, final int nSteps,
			final int nSimulations) throws Exception {
		double estimatedPrice = 0.0;
		
		List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
	    
		for(int i=0; i<nSimulations; i++) {
	      partitions.add(new Callable<Double>() {
	        public Double call() throws Exception {
	          return algorithm(callPutFlag, s, x, r, t, b, v, nSteps);
	        }        
	      });
	    }
	    
	    ExecutorService executorPool = Executors.newFixedThreadPool(this.getPoolSize());    
	    List<Future<Double>> stValues = executorPool.invokeAll(
	      partitions, 10000, TimeUnit.SECONDS);
	    executorPool.shutdown();
	      
	    double sum=0.0;
	    for(Future<Double> value : stValues) { 
	      sum += value.get(); 
	    }
	    
	    estimatedPrice = Math.exp(-r * t) * (sum / nSimulations);
		return estimatedPrice;
	}

	/**
	 * Function to be called for the threads 
	 * @return value of one simulation
	 */
	public final double algorithm(String callPutFlag, double s, double x, double r,
			double t, double b, double v, int nSteps)
			throws Exception {
		double dt = 0.0, st = 0.0, drift = 0.0, vsqrdt = 0.0;
		int j = 0, z = 0;
		dt = t / nSteps;
		drift = (b - Math.pow(v, 2) / 2) * dt;
		vsqrdt = v * Math.sqrt(dt);

		if (callPutFlag.equals("c"))
			z = 1;
		else if (callPutFlag.equals("p"))
			z = -1;
		else
			System.out.println("File to read doesn't have the right format.");

		Random random = new Random();

		st = s;
		for (j = 1; j <= nSteps; j++) {
			st = st * Math.exp(drift + vsqrdt * random.nextGaussian());
		}
		return Math.max(z*(st-x), 0);
	}

}
